package com.ruoyi.material.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.material.domain.BaseFlieRecord;
import com.ruoyi.material.domain.constant.FileConstant;
import com.ruoyi.material.domain.vo.BaseMaterialTreeVo;
import com.ruoyi.material.mapper.BaseFlieRecordMapper;
import com.ruoyi.material.util.CompressUtils;
import com.ruoyi.material.util.FileUtil;
import com.ruoyi.material.util.RandomlyGeneratedUtil;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.material.mapper.BaseMaterialMapper;
import com.ruoyi.material.domain.BaseMaterial;
import com.ruoyi.material.service.IBaseMaterialService;

import static sun.security.krb5.Confounder.longValue;

/**
 * 目录管理Service业务层处理
 * 
 * @author cyw
 * @date 2021-07-15
 */
@Service
public class BaseMaterialServiceImpl implements IBaseMaterialService 
{
    private Logger log = LoggerFactory.getLogger(BaseMaterial.class);
    @Autowired(required=false)
    private BaseMaterialMapper baseMaterialMapper;

    @Autowired(required=false)
    private BaseFlieRecordMapper baseFlieRecordMapper;


    /**
     * 查询目录管理
     * 
     * @param id 目录管理ID
     * @return 目录管理
     */
    @Override
    public BaseMaterial selectBaseMaterialById(String id)
    {
        return baseMaterialMapper.selectBaseMaterialById(id);
    }

    /**
     * 查询目录管理列表
     * 
     * @param baseMaterial 目录管理
     * @return 目录管理
     */
    @Override
    public List<BaseMaterial> selectBaseMaterialList(BaseMaterial baseMaterial)
    {
        return baseMaterialMapper.selectBaseMaterialList(baseMaterial);
    }

    /**
     * 新增目录管理
     * 
     * @param baseMaterial 目录管理
     * @return 结果
     */
    @Override
    public int insertBaseMaterial(BaseMaterial baseMaterial)
    {
        baseMaterial.setCreateTime(DateUtils.getNowDate());
        return baseMaterialMapper.insertBaseMaterial(baseMaterial);
    }

    /**
     * 修改目录管理
     * 
     * @param baseMaterial 目录管理
     * @return 结果
     */
    @Override
    public int updateBaseMaterial(BaseMaterial baseMaterial)
    {
        baseMaterial.setUpdateTime(DateUtils.getNowDate());
        return baseMaterialMapper.updateBaseMaterial(baseMaterial);
    }

    /**
     * 批量删除目录管理
     * 
     * @param ids 需要删除的目录管理ID
     * @return 结果
     */
    @Override
    public int deleteBaseMaterialByIds(String[] ids)
    {
        return baseMaterialMapper.deleteBaseMaterialByIds(ids);
    }

    /**
     * 删除目录管理信息
     * 
     * @param id 目录管理ID
     * @return 结果
     */
    @Override
    public int deleteBaseMaterialById(String id)
    {

        return baseMaterialMapper.deleteBaseMaterialById(id);
    }

    public List<BaseMaterial> selectBaseFlieRecordByPcode(String pcode){
        return null;
    }


    /**
     * 构建前端所需要下拉树结构
     *
     *
     * @return 下拉树结构列表
     */
    @Override
    public List<BaseMaterialTreeVo> buildMaterialTreeSelect()
    {
        BaseMaterial baseMaterial = new BaseMaterial();
        baseMaterial.setType("folder");
        baseMaterial.setDelFlag("0");
        List<BaseMaterial> materials = baseMaterialMapper.selectBaseMaterialList(baseMaterial);

        return  buildMaterialTree(materials);
    }

    /**
     * 获取该目录下所有的文件压缩后的打包地址
     * @error 还无法做到根据系统进行上传与下载
     * @param fileCode 目录的文件码
     * @return 压缩包的地址
     */
    public String downloadMaterialByCode(Long fileCode) throws IOException {
        List<BaseMaterial> allBaseMaterial = baseMaterialMapper.selectBaseMaterialList(new BaseMaterial());
        BaseMaterial selectBaseMaterial = new BaseMaterial();
        selectBaseMaterial.setFileCode(fileCode);
        BaseMaterial downMaterial = baseMaterialMapper.selectBaseMaterialList(selectBaseMaterial).get(0);
        // 所有文件的所对应的路径
        Map<String ,List<BaseMaterial>> fileMap = new HashMap<>();
        for(int i = 0;i<allBaseMaterial.size();i++){
            List<BaseMaterial> children = fileMap.getOrDefault(allBaseMaterial.get(i).getpCode(),new ArrayList<>());
            children.add(allBaseMaterial.get(i));
            fileMap.put(allBaseMaterial.get(i).getpCode(),children);
        }
        // 文件目录 真实文件地址
        Map<String,Set<BaseFlieRecord>> downloadMap = new HashMap<>();
        StringBuffer buffer = new StringBuffer();
        buffer.append(downMaterial.getName());
        BFSDownloadMap(fileMap,downMaterial.getCode(),downloadMap,buffer);
        String materialPath = createMaterial(downloadMap,downMaterial.getName());
        log.debug("压缩路径 [{}]",materialPath+".zip");
        CompressUtils.zip(materialPath,materialPath+".zip");
        return materialPath+".zip";
    }
    /**
     *  压缩目录
     */


    /**
     * 创建目录和文件在实体空间内
     * @param downloadMap
     * @param materialName 目录名称
     * @return 创建后的目录后的路径
     */
    private String createMaterial(Map<String,Set<BaseFlieRecord>> downloadMap,String materialName){
        log.debug("遍历获取的目录 【{}】",downloadMap);
        Set<String> keys = downloadMap.keySet();
        // 本地资源路径
        String localPath = RuoYiConfig.getProfile();
        // 数据库资源地址
        StringBuffer filePath = new StringBuffer();
        String pathMaterial = localPath+File.separator+materialName;
        // 删除之前的文件
        FileUtil.deleteDirectory(pathMaterial);
        FileUtil.deleteFile(pathMaterial+".zip");
        filePath.append(localPath);
        // 创建文件夹
        log.debug("生成全部目录的路径 【{}】",keys);
        for (String path:keys){
            FileUtil.createDir(localPath+ File.separator+path);
        }
        // 复制文件
        for (String path:keys){
            Set<BaseFlieRecord> files = downloadMap.get(path);
            for (BaseFlieRecord file: files){
                log.debug("复制后的文件 【{}】", localPath+File.separator+path+File.separator+file.getOriginalFileName());
                log.debug("源文件文件 【{}】", FileUtil.getRealFilePath(file.getPathFileName()));
                FileUtil.copyNio(FileUtil.getRealFilePath(file.getPathFileName()), localPath+File.separator+path+File.separator+file.getOriginalFileName());
            }
        }
        log.debug("复制后的文件地址 【{}】",localPath+File.separator+materialName);
        return localPath+File.separator+materialName;
//        return null;
    }

    /**
     * 广度优先遍历需要下载的树
     * @param fileMap 所有文件信息
     * @param code  当前目录的code
     * @param path  当前目录的路径
     * @param downloadMap 存储目录与真实文件对应的地址
     */
    private void BFSDownloadMap(Map<String,List<BaseMaterial>> fileMap ,String code,Map<String,Set<BaseFlieRecord>> downloadMap,StringBuffer path){
        List<BaseMaterial> curMaterial = fileMap.get(code);
        if (curMaterial == null){
            return;
        }
        if (curMaterial.size() == 0){
            return;
        }
        for (Iterator<BaseMaterial> iterator = curMaterial.iterator();iterator.hasNext();){

            BaseMaterial children = iterator.next();
            // 当是目录开始递归这个目录
            // 不是目录的时候开始存放记录
            if (!children.getType().startsWith(FileConstant.FOLDER_TYPE) && children.getFileCode() != null){
                BaseFlieRecord file = baseFlieRecordMapper.selectBaseFlieRecordById(children.getFileCode());
                Set<BaseFlieRecord> childrenPath = downloadMap.getOrDefault(path.toString(),new HashSet<BaseFlieRecord>());
                childrenPath.add(file);
                downloadMap.put(path.toString(),childrenPath);
            }else if (children.getType().startsWith(FileConstant.FOLDER_TYPE) && children.getFileCode()!= null){
                path.append(File.separator);
                path.append(children.getName());
                downloadMap.put(path.toString(),new HashSet<BaseFlieRecord>());
                BFSDownloadMap(fileMap,children.getCode(),downloadMap,path);
                path.delete(path.length()-children.getName().length()-1,path.length());
            }
        }


    }
    /**
     * 删除目录管理信息
     *
     * @param code 目录管理code
     *
     * @return 结果
     */
    public int deleteBaseMaterialByCode(String code){
        BaseMaterial baseMaterial = new BaseMaterial();
        List<BaseMaterial> materials = baseMaterialMapper.selectBaseMaterialList(baseMaterial);
        BaseMaterial top = baseMaterialMapper.selectBaseMaterialByCode(code);
        Map<String ,List<BaseMaterial>> map = new HashMap<>();
        for(int i = 0;i<materials.size();i++){
            List<BaseMaterial> children = map.getOrDefault(materials.get(i).getpCode(),new ArrayList<>());
            children.add(materials.get(i));
            map.put(materials.get(i).getpCode(),children);
        }
        Set<String> mSet = new HashSet<>();
        Set<Integer> fSet = new HashSet<>();
        // 顶级节点
        mSet.add(top.getId());
        fSet.add(top.getFileCode().intValue());
        fSet.add(top.getTabCode().intValue());
        List<BaseMaterial> topNode = map.get(code);
        for (BaseMaterial node: topNode){
            recursionFn(map,node.getCode(),mSet,fSet);
            mSet.add(node.getpCode());
            fSet.add(node.getFileCode().intValue());
            fSet.add(node.getTabCode().intValue());
        }
        int i = baseMaterialMapper.deleteBaseMaterialByIds(mSet.toArray(new String[mSet.size()]));
        i += baseFlieRecordMapper.deleteBaseFlieRecordByIds(fSet.toArray(new Long[fSet.size()]));
        return  i;
    }
    public void recursionFn(Map<String ,List<BaseMaterial>> map ,String pcode,Set<String> mSet,Set<Integer> fSet){
        // 得到子节点列表
        List<BaseMaterial> topNode = map.get(pcode);
        if (topNode == null || topNode.isEmpty()){
            return;
        }
        for (BaseMaterial node: topNode){
            recursionFn(map,node.getCode(),mSet,fSet);
            mSet.add(node.getpCode());
            fSet.add(node.getFileCode().intValue());
            fSet.add(node.getTabCode().intValue());
        }
    }

    /**
     * 构建前端所需要树结构
     *
     * @param
     * @return 树结构列表
     */
    public List<BaseMaterialTreeVo> buildMaterialTree(List<BaseMaterial> materials){
        Map<String ,List<BaseMaterial>> map = new HashMap<>();
        List<BaseMaterialTreeVo> res = new ArrayList<>();
        for(int i = 0;i<materials.size();i++){
            List<BaseMaterial> children = map.getOrDefault(materials.get(i).getpCode(),new ArrayList<>());
            children.add(materials.get(i));
            map.put(materials.get(i).getpCode(),children);
        }
        // 顶级节点
        List<BaseMaterial> topNode = map.get("-1");
        if (topNode == null){
            BaseMaterialTreeVo  tree = new BaseMaterialTreeVo();
            tree.setLabel("-1");
            tree.setLabel("root");
            res.add(tree);
            return res;
        }
        BaseMaterialTreeVo tree = new BaseMaterialTreeVo();
        tree.setId("-1");
        tree.setLabel("root");
        res.add(tree);
        for(int i=0;i<topNode.size();i++){
            BaseMaterialTreeVo treeNode = new BaseMaterialTreeVo();
            treeNode.setId(topNode.get(i).getCode());
            treeNode.setLabel(topNode.get(i).getName());
            recursionFn(map,topNode.get(i).getCode(),treeNode);
            tree.getChildren().add(treeNode);
        }
        return res;

    }

    /**
     * 递归列表
     */
    private void recursionFn(Map<String,List<BaseMaterial>> map, String pcode,BaseMaterialTreeVo node)
    {
        // 得到子节点列表
        List<BaseMaterial> childList = map.get(pcode);
        List<BaseMaterialTreeVo> trees = new ArrayList<>();
        if (childList == null || childList.isEmpty()){
            return;
        }else{
            for(BaseMaterial baseMaterial : childList){
                BaseMaterialTreeVo childNode = new BaseMaterialTreeVo();
                childNode.setId(baseMaterial.getCode());
                childNode.setLabel(baseMaterial.getName());
                trees.add(childNode);
            }
            node.setChildren(trees);
            for (BaseMaterialTreeVo tree : trees)
            {
                if (map.containsKey(tree.getId())){
                    recursionFn(map,tree.getId(),tree);
                }
            }
        }

    }



    /**
     * 批量添加
     * @param list
     * @return
     */
    @Override
    public int addBaseMaterialList(List<BaseMaterial> list){
        return baseMaterialMapper.addBaseMaterialList(list);
    }

    /**
     * 假删除文件记录
     *
     * @param list 文件记录ID
     * @param updateBy
     * @return 结果
     */
    @Override
    public int delBaseMaterialListByFileCode(@Param("list") List<Integer> list,@Param("updateBy") String updateBy){
        return baseMaterialMapper.delBaseMaterialListByFileCode(list,updateBy);
    }


    /**
     * 假删除之前需要先查符合p_code的记录 ,返回记录List
     *
     * @param p_code 上一级的code,本级的p_code
     * @return 结果
     */
    @Override
    public List<BaseMaterial> selectBaseMaterialIdsByPcode(String p_code){
        return baseMaterialMapper.selectBaseMaterialIdsByPcode(p_code);
    }
    /**
     * 7.17假删除之前需要先查符合p_code的记录 ,返回记录List
     *
     * @param p_code 上一级的code,本级的p_code
     * @return 结果
     */
    @Override
    public List<BaseMaterial> selectBMFilecodesByPcode(String p_code){
        return baseMaterialMapper.selectBMFilecodesByPcode(p_code);
    }
    /**
     * 查找p_code符合的文件夹 ,返回记录List
     *
     * @param p_code 上一级的code,本级的p_code
     * @return 结果
     */
    @Override
    public List<BaseMaterial> selectBMFoldersByPcode(String p_code){
        return baseMaterialMapper.selectBMFoldersByPcode(p_code);
    }
    /**
     * 根据已经找的的id、filecode的map进行批量删除
     *
     * @param list 上一级的code,本级的p_code
     * @param updateBy
     * @return 结果
     */
    @Override
    public int delBaseMaterialList(@Param("list") List<String> list,@Param("updateBy") String updateBy){
        return baseMaterialMapper.delBaseMaterialList(list,updateBy);
    }
    /**
     * 已知要删除的是文件的id,获取material表中文件的id,filecode(即已知的id)
     *
     * @param file_code 上一级的code,本级的p_code
     * @return 结果
     */
    @Override
    public BaseMaterial selectBMByFileCode(Integer file_code){
        return baseMaterialMapper.selectBMByFileCode(file_code);
    }
    /**
     * 统计某种文件数量
     *
     * @param type 类型
     * @return 结果
     */
    @Override
    public int countUncertainFiles(String type)
    {
        return baseMaterialMapper.countUncertainFiles(type);
    }

    /**
     * 统计文件数量
     *
     * @return 结果
     */
    @Override
    public int countBaseMaterialFiles(){
        return baseMaterialMapper.countBaseMaterialFiles();
    }
    /**
     * 统计文件夹数量
     *
     * @return 结果
     */
    @Override
    public int countBaseMaterialFolders(){
        return baseMaterialMapper.countBaseMaterialFolders();
    }

    /**
     * 按照日期统计 近30天 文件数量
     *
     * @return 结果
     */
    @Override
//    public Map<String,String> countBaseMaterialFilesByDate(){
    public List countBaseMaterialFilesByDate(){
        return baseMaterialMapper.countBaseMaterialFilesByDate();
    }

//    /**
//     * 修改文件夹路径
//     *
//     * @param baseMaterial 文件夹
//     * @return 结果
//     */
//    @Override
//    public int updateBMFolderPcodeById(BaseMaterial baseMaterial){
//        return baseMaterialMapper.updateBMFolderPcodeById(baseMaterial);
//    }

    /**
     * 修改
     *
     * @param baseMaterial
     * @return 结果
     */
    public int updateBaseMaterialByFile_code(BaseMaterial baseMaterial){
        return baseMaterialMapper.updateBaseMaterialByFile_code(baseMaterial);
    }

    /**
     * @param file_code
     * @param tab_code
     * @return 结果
     */
    @Override
    public int updateBaseMaterialByFileCode(@Param("file_code") Integer file_code, @Param("tab_code") Integer tab_code){
        return  baseMaterialMapper.updateBaseMaterialByFileCode(file_code,tab_code);
    }

    /**
     * 通过文件code查找文件
     * @param code
     * @return
     */
    public BaseMaterial selectByCode(String code){
        return baseMaterialMapper.selectByCode(code);
    }


    /**
     * 根据给定路径创建目录
     * @param path 文件路径
     * @param pcode 此目录所在的目录标记
     * @return 返回创建后的Pcode
     */
    public String createMaterialByFilePath(String path,String pcode,String createBy) throws Exception {

        if (path == null || pcode == null){
            return null;
        }
        path = path.substring(1,path.length());
        String[] paths = path.split("/");
        BaseMaterial parentMaterial = baseMaterialMapper.selectBaseMaterialById(pcode);
        if (parentMaterial == null){
            throw new Exception("根据路径创建文件失败，无法查询当前目录的所在父目录，请检查父目录是否存在");
        }
        //按照名字来搜索
        for (int i=0;i<paths.length;i++){
            log.debug("依次创建目录 PCODE {}",pcode);
            log.debug("依次创建目录 path {}",path);
            log.debug("依次创建目录 paths {} ",paths[i]);
            BaseMaterial curMaterial = baseMaterialMapper.selectMaterialByNameAndPcode(paths[i],pcode);
            if (curMaterial == null){
                pcode = insertFileRecord(paths[i],FileConstant.FOLDER_TYPE,0l,pcode,createBy);
            }else{
                pcode = curMaterial.getCode();
            }


        }
        return pcode;
    }

    /**
     * 在数据库中插入一条文件目录记录
     * @param fileName
     * @param fileType
     * @param fileSize
     * @param pcode
     * @param createBy
     * @return  创建目录的标识符
     */
    public String insertFileRecord( String fileName,String fileType,Long fileSize,String pcode,String createBy){
        return insertFileRecord(fileName,fileType,fileSize,pcode,createBy,"");
    }

    /**
     * 在数据库中插入一条文件记录
     */
    public String insertFileRecord( String fileName,String fileType,Long fileSize,String pcode,String createBy,String accessPath){
        log.debug("文件名称 【{}】",fileName);
        log.debug("文件pcode 【{}】",pcode);
        BaseFlieRecord record = new BaseFlieRecord();
        record.setFileSize(fileSize)
                .setOriginalFileName(fileName)
                .setPathFileName(Constants.RESOURCE_PREFIX+accessPath)
                .setContentType(fileType)
                .setCreateBy(createBy);
        record.setCreateTime(DateUtils.getNowDate());
        // todo  id 文件ID自定义
        baseFlieRecordMapper.insertBaseFlieRecord(record);
        record = baseFlieRecordMapper.selectBaseFlieRecordList(record).get(0);
        BaseMaterial material = new BaseMaterial();
        material.setpCode(pcode);
        material.setFileCode(record.getId());
        material.setName(fileName);
        material.setType(fileType);
        material.setCode(RandomlyGeneratedUtil.GeneratID());
        material.setCreateTime(record.getCreateTime());
        material.setId(material.getCode());
        material.setFileCode(record.getId());
        material.setCreateBy(createBy);
        material.setCreateTime(DateUtils.getNowDate());
        material.setDelFlag("0");
        baseMaterialMapper.insertBaseMaterial(material);

        return material.getCode();
    }
    /**
     * 在数据库中插入一条文件记录
     */
    public String insertFileRecord( String fileName,String fileType,Long fileSize,String pcode,String createBy,String accessPath,Date createTime){
        log.debug("文件名称 【{}】",fileName);
        log.debug("文件pcode 【{}】",pcode);
        BaseFlieRecord record = new BaseFlieRecord();
        record.setFileSize(fileSize)
                .setOriginalFileName(fileName)
                .setPathFileName(accessPath)
                .setContentType(fileType)
                .setCreateBy(createBy);
        record.setCreateTime(createTime);
        // todo  id 文件ID自定义
        baseFlieRecordMapper.insertBaseFlieRecord(record);
        record = baseFlieRecordMapper.selectBaseFlieRecordList(record).get(0);
        BaseMaterial material = new BaseMaterial();
        material.setpCode(pcode);
        material.setFileCode(record.getId());
        material.setName(fileName);
        material.setType(fileType);
        material.setCode(RandomlyGeneratedUtil.GeneratID());
        material.setCreateTime(record.getCreateTime());
        material.setId(material.getCode());
        material.setFileCode(record.getId());
        material.setCreateBy(createBy);
        material.setCreateTime(DateUtils.getNowDate());
        material.setDelFlag("0");
        baseMaterialMapper.insertBaseMaterial(material);

        return material.getCode();
    }




}
